package com.google.sydym6.logic.managers;

import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Calendar;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import javazoom.jl.decoder.JavaLayerException;

import com.google.sydym6.logic.database.ISongDAO;
import com.google.sydym6.logic.database.SongDAO;
import com.google.sydym6.logic.database.data.IRadioData;
import com.google.sydym6.logic.database.data.ISongData;
import com.google.sydym6.logic.database.data.SongData;
import com.google.sydym6.logic.managers.recognizing.IRecognizeAgent;
import com.google.sydym6.logic.managers.recognizing.IScrobbler;
import com.google.sydym6.logic.managers.recognizing.RecognizeAgent;
import com.google.sydym6.logic.observers.IPlayerStateObservable;
import com.google.sydym6.logic.observers.IPlayerStateObserver;
import com.google.sydym6.logic.observers.IRecognizeStatusObservable;
import com.google.sydym6.logic.observers.IRecognizeStatusObserver;
import com.gracenote.mmid.MobileSDK.GNOperations;
import com.gracenote.mmid.MobileSDK.GNSearchResponse;
import com.gracenote.mmid.MobileSDK.GNSearchResult;
import com.gracenote.mmid.MobileSDK.GNSearchResultReady;

import android.content.Context;
import android.media.AudioManager;
import android.media.AudioManager.OnAudioFocusChangeListener;
import android.media.MediaPlayer;
import android.util.Log;
 
public class RadioManager implements IRadioManager, IRecognizeStatusObservable, IRecognizeStatusObserver, IPlayerStateObservable,
									 AudioManager.OnAudioFocusChangeListener{
	
	private static final int DEFAULT_RADIO_TIMER_PERIOD = 10 * 1000;
	public static final String RECOGNIZING_SUCCESS = "Success";
	public static final String LOADING_SUCCESS = "Complete loading";
	public static final String RADIO_LOADING = "Loading";
	
	private volatile boolean isRecognizing = false;
	private boolean isPlaying = false;
	
	private Set<IRecognizeStatusObserver> recognizeStatusObservers;
	private Set<IPlayerStateObserver> playerStateObservers;
	
	private int radioTimerPeriod;
	
	private MediaPlayer radioPlayer;
	private ScheduledThreadPoolExecutor radioTimer;
	
	private IRadioData radioInfo;
	private ISongDAO songDAO;
	private List<ISongData> songList;
	
	private IRecognizeAgent recognizeAgent;
	//private ISongManager songManager;
	private IScrobbler scrobbler;
	
	private String previousArtist;
	private String previousTrack;
	private String recognizeStatus;
	private Context context;

	public RadioManager(ISongManager songManager, Context context, int radioTimerPeriod) {
		this.context = context;
		this.radioTimerPeriod = radioTimerPeriod;
		//this.songManager = songManager;
		recognizeAgent = new RecognizeAgent(context);
		
		recognizeStatusObservers = new HashSet<IRecognizeStatusObserver>();
		playerStateObservers = new HashSet<IPlayerStateObserver>();
		
		songDAO = new SongDAO(context);
		
		IRecognizeStatusObservable recognizeAgentStatusObservable = (IRecognizeStatusObservable) recognizeAgent;
		recognizeAgentStatusObservable.addObserver((IRecognizeStatusObserver)this);
	}

	public RadioManager(ISongManager songManager, Context context) {
		this(songManager, context, DEFAULT_RADIO_TIMER_PERIOD);
	}

	@Override
	public void setRadio(IRadioData radioInfo) {
		this.radioInfo = radioInfo;
		songList = songDAO.getHistory(radioInfo);
		System.out.println("Installed new radio: " + radioInfo.getId());
		
		if (radioPlayer != null)
			stopRadio();

		if (songList != null && songList.size() > 0) {
			previousArtist = songList.get(songList.size() - 1).getArtist();
			previousTrack = songList.get(songList.size() - 1).getTitle();
		} else {
			previousArtist = null;
			previousTrack = null;
		}
	}

	@Override
	public void playRadio() throws IllegalArgumentException, SecurityException, IllegalStateException, IOException {
		
		AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
		int result = audioManager.requestAudioFocus((OnAudioFocusChangeListener) this, AudioManager.STREAM_MUSIC,
		    AudioManager.AUDIOFOCUS_GAIN);

		if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
		    return;
		}
		
		//if (songManager.isPlaying())
		//	songManager.pauseSong();

		isPlaying = true;
		notifyPlayerStateObservers();
		
		recognizeStatus = RADIO_LOADING;
		notifyRecognizeStatusObservers();
		
		radioPlayer = new MediaPlayer();
		radioPlayer.setDataSource(radioInfo.getUrl());
		radioPlayer.prepare();
		
		recognizeStatus = LOADING_SUCCESS;
		notifyRecognizeStatusObservers();
		
		radioPlayer.start();
		
		if (!radioInfo.isTalk()) {
			radioTimer = new ScheduledThreadPoolExecutor(1);
			radioTimer.scheduleWithFixedDelay(new Thread() {

				@Override
				public void run() {
					try {
						recognize();
						System.out.println("IsRecognizing value: " + isRecognizing);
					} catch (MalformedURLException e) {
						e.printStackTrace();
					} catch (JavaLayerException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				
			}, 0, radioTimerPeriod, TimeUnit.MILLISECONDS);
		}
	}

	@Override
	public void stopRadio() {
		if (radioPlayer != null)
			radioPlayer.stop();
		
		if ((!radioInfo.isTalk()) && (radioTimer != null)) {
			radioTimer.shutdownNow();
			cancelRecognize();
		}
		
		isPlaying = false;
		notifyPlayerStateObservers();
		
		recognizeStatus = "Stopped";
		notifyRecognizeStatusObservers();
	}

	@Override
	public void recognize() throws MalformedURLException, JavaLayerException, IOException, InterruptedException {
		
		if (!isRecognizing) {
			synchronized (this) {
				if (!isRecognizing) {
					isRecognizing = true;
					
					GNSearchResult result = recognizeAgent.recognize(radioInfo.getUrl());

					if (result.isFailure()) {
						recognizeStatus = String.format("[%d] %s", result.getErrCode(),
								result.getErrMessage());
						
						//songManager.setSong(null, null);
						//notifyRecognizeStatusObservers();
						//System.out.println(recognizeStatus);
					} else {
						if (result.isFingerprintSearchNoMatchStatus()) {
							recognizeStatus = "Music Not Identified";
							
							//songManager.setSong(null, null);
							//notifyRecognizeStatusObservers();
							//System.out.println(recognizeStatus);
						} else {
							GNSearchResponse bestResponse = result.getBestResponse();

							String artist = bestResponse.getArtist();
							String track = bestResponse.getTrackTitle();
					
							recognizeStatus = RECOGNIZING_SUCCESS;
							System.out.println(recognizeStatus);
							System.out.println(artist + " " + track);

							//if(!artist.equals(previousArtist) || !track.equals(previousTrack)) {
							//	songManager.setSong(artist, track);
							//}
							
							
							if(!artist.equals(previousArtist) || !track.equals(previousTrack)) {
								scrobbler.scrobble(artist, track);
								
								ISongData songInfo = new SongData(-1, radioInfo.getTitle(), artist, track, bestResponse.getTrackId(), Calendar.getInstance().getTime().toString());
								songList.add(songInfo);
								songDAO.insert(songInfo);
								
								previousArtist = artist;
								previousTrack = track;
							}
						}
					}
					
					isRecognizing = false;
					notifyRecognizeStatusObservers();
				}
			}
		}
		System.out.println("Exiting recognize.");
	}
	
	@Override
	public void cancelRecognize() {
		GNOperations.cancel((GNSearchResultReady) recognizeAgent);
		recognizeStatus = "Canceled :(";
		isRecognizing = false;
		System.out.println(recognizeStatus);
		notifyRecognizeStatusObservers();
	}

	@Override
	public void setRecognizePeriod(int period) {
		radioTimerPeriod = period;
	}

	@Override
	public boolean isPlaying() {
		return isPlaying;
	}

	@Override
	public void addObserver(IRecognizeStatusObserver o) {
		recognizeStatusObservers.add(o);
	}

	@Override
	public void removeObserver(IRecognizeStatusObserver o) {
		recognizeStatusObservers.remove(o);
	}

	@Override
	public void notifyRecognizeStatusObservers() {
		for(IRecognizeStatusObserver o : recognizeStatusObservers)
			o.updateRecognizeStatus();
	}

	@Override
	public String getRecognizeStatus() {
		return recognizeStatus;
	}

	@Override
	public void updateRecognizeStatus() {
		recognizeStatus = recognizeAgent.getRecognizeStatus();
		System.out.println("Recognizestatus is: " + recognizeStatus);
		notifyRecognizeStatusObservers();
	}

	@Override
	public void addObserver(IPlayerStateObserver o) {
		playerStateObservers.add(o);
	}

	@Override
	public void removeObserver(IPlayerStateObserver o) {
		playerStateObservers.remove(o);
	}

	@Override
	public void notifyPlayerStateObservers() {
		for (IPlayerStateObserver o : playerStateObservers)
			o.updatePlayerState();
	}

	@Override
	public IRadioData getRadio() {
		return radioInfo;
	}

	@Override
	public boolean isInitialized() {
		return !(radioPlayer == null);
	}

	@Override
	public void setScrobbler(IScrobbler scrobbler) {
		this.scrobbler = scrobbler;
	}

	@Override
	public List<ISongData> getSongHistory() {
		return songList;
	}

	@Override
	public void onAudioFocusChange(int focusChange) {
		Log.v("AudioManager", "" + focusChange);
		if (focusChange == -1) {
			stopRadio();
		}
	}

	@Override
	public String getRecognizedArtistTitle() {
		if (isRecognizing) {
			return null;
		} else {
			return previousArtist + " - " + previousTrack;
		}
	}
}
